package batchjob;

import java.io.FileReader;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.util.*;
import java.text.SimpleDateFormat;
import EA.BatchJobEA;
import EA.recording.ConvertToText;


/** The batchjob scheduler can be used to schedule larger runs of EA's.
    It implements a number of methods useful for handling parameters,
    dumping plots, generating movies and pictures, profiling the 
    EA it is applied to, etc.
*/
public class BatchJobScheduler
{

  /** The EA this scheduler is handling. */
  public Object theEA;
    
  /** The filename for the input file to the scheduler. */
  public String batchjobfilename;

  public String[] allbatchjobfiles;

  /** Give estimations of the time of completions. Default = true */
  public boolean verbosemode = true;

  /** Number of runs per set of parameters. Default = 1 */
  public int BJSruns = 1;

  /** How to arrange the data when stored. (Default=0)<br>
      0 = Do not store any data for this run.<br>
      1 = Save all datafiles in the same directory specified by BJSdatadir.<br>
      2 = Create a subdir for each runseries (a runseries consist of 
      BJSruns runs) and save the files in that directory.<BR>
  */
  public int BJSsavemethod = 0;

  /** Save each run in its own file. (Default = false) */
  public boolean BJSsavesingle = false;

  /** Save each runseries (BJSruns) in its own file. (Default = true) */
  public boolean BJSsaverunseries = false;

  /** Save all runs in the same file. (Default = false) */
  public boolean BJSsaveallinone = false;

  /** Autonaming of generated files. (Default=0)<br>
      0 = Append run number to filename. The necessary number of leading 
      zero's will be prepended in the number.<br>
      FORMAT:  <basefilename>_<runnumber>.dat<br>
      EXAMPLE: multipopstat_0037.dat<br>
      1 = Append grouped run number to filename. This mode will group the
      files according to the value given in BJSruns.<br>
      FORMAT:  <basefilename>_<overallrun>_<current BJSrun>.dat<br>
      EXAMPLE: multipopstat_03_09.dat<br>
      2 = Append run completion datetime to filename.<br>
      FORMAT:  <basefilename>_<completion datetime>.dat<br>
      EXAMPLE: multipopstat_11-08-1999-12:34:52.dat<br>
      3 = Append run completion datetime (sorted and shortened) to filename.
      By using this format the files will be ordered in increasing order
      when the directory is listed (ls).<br>
      FORMAT:  <basefilename>_<completion datetime>.dat<br>
      EXAMPLE: multipopstat_19990811123452.dat<br>
  */
  public int BJSautonaming = 0;

  /** Where to store the data. (Default="results/")<br>
   */
  public String BJSdatadir = "results/";

  /** Create a directory for each time you start the algorithm. 
      (default = 0)<br>
      0 = Do not create a directory.<br>
      1 = Append date to BJSdatadir. (See below)<br>
      2 = Append sorted and shorteded date to BJSdatadir.<br><br>
      This works the following way: <br>
      If BJSdatadir = "results/", BJScreatedir = "multi" and BJScreatedir = 2 then the data 
      will be stored in e.g. "results/multi_19990811123452/"
  */
  public int BJScreatedirmethod = 0;

  /** The basename used when creating the directory. (Default = "")*/
  public String BJScreatedir = "";

  /** Delimiter between items/columns. */
  public String BJScolumndelim = "\t";

  /** Delimiter between rows. */
  public String BJSrowdelim = "\n";


  /** Contains the order of jobs according to the batch inputfile. */
  Vector batchjoborder;

  /** Contains the various parameters from the batch inputfile. */
  public Hashtable batchjobparams;

  /** Write a script for this run. */
  boolean BJSwritescript = false;
  /** Build a script to process the data. The scheduler will automatically
      cd into the datadir. */
  public String BJSscriptcommand = "";

  /** Contains the current variables to step through. */
  Object[] currentsteppers;

  BJSStepline[] currentsteplines;

  private RKUVector rawbatchjobfile;

  long startmillis, endmillis;

  /** Create a new scheduler with a given EA.
      @param anEA The EA this scheduler handles. This object must implement
      the BatchJob interface.
      @see BatchJob
  */
  public BatchJobScheduler(Object anEA)
    {
      super();
      theEA = anEA;
      batchjoborder = new Vector();
      batchjobparams = new Hashtable();
      currentsteppers = new Object[0];
      rawbatchjobfile = new RKUVector();

    }

  /** Start the scheduler. This method will load the settingsfile, setup
      the various runs and then perform them.<br>
      It currently supports the following arguments:<br>
      -f <filenames> The batch files you want to run.
      -t estimate time of completion.
      @param arguments The arguments passed from the command line. The method
      will extract the various settings from the array.
  */
  public void startScheduler(String[] arguments)
    {
      int i;
      FileWriter finalscript = null;
      String finalscriptname = "";
      StringTokenizer countback;
      String backtrack;
      FileWriter fw;
      String progressfilename;

      parseArgs(arguments);

      if (allbatchjobfiles == null || allbatchjobfiles.length==0) {
	System.out.println("No batchjob file given. Use option -f <filenames>");
	System.exit(0);
      }

      try {
	if (BJSwritescript) {
	  finalscriptname = "Datascript_"+getNowCompressed()+".bat";
	  finalscript = new FileWriter(finalscriptname);
	  finalscript.write("#!/usr/local/bin/tcsh\n");
	};

	for (i=0;i<allbatchjobfiles.length;i++) {

	  batchjoborder = new Vector();
	  batchjobparams = new Hashtable();
	  currentsteppers = new Object[0];
	  rawbatchjobfile = new RKUVector();
	  currentsteplines = null;

	  batchjobfilename = allbatchjobfiles[i];
	  loadBatchFile(batchjobfilename);
	  calcTotalRuns();
	  
	  //	lastmill = System.currentTimeMillis();
	  
	  // Prepare to save the data
	  if (BJSsavemethod!=0)
	    prepareSave();
	  
	  if (allbatchjobfiles.length>1) {
	    System.out.println("\nExecuting \""+batchjobfilename+"\"");
	  }

	  ((BatchJobEA)theEA).initializeBatchJobs();
	  startmillis = System.currentTimeMillis();
	  firstmill = 0;
	  currentrun = 0;
	  performJob(0);
	  endmillis = System.currentTimeMillis();
	  ((BatchJobEA)theEA).finalizeBatchJobs();
	  
	  if (verbosemode) {
	    System.out.print("\nRuns completed in "+RKUStringUtils.timeDifference(startmillis, endmillis));
	    if (BJSsavemethod!=0) {
	      System.out.print(", data can be found in: "+BJSdatadir);
	     try {
	      // added by TK 23/01/2000
	      progressfilename = "runprogress";
	      fw = new FileWriter(BJSdatadir+progressfilename,currentrun>1);
		  fw.write("\nRuns completed in "+RKUStringUtils.timeDifference(startmillis, endmillis)+"\n");
	      fw.close();
       } catch (Exception ee) { System.out.println(ee);};
	    }
	    System.out.println("");
	  }

	  if (BJSwritescript) {
	    countback = new StringTokenizer(BJSdatadir,"/");
	    backtrack = "";
	    while(countback.hasMoreTokens()) {
	      backtrack += "../";
	      countback.nextToken();
	    }
	    if (backtrack.length()>0) {
	      finalscript.write("cd "+BJSdatadir+"; "+BJSscriptcommand+" ; cd "+backtrack+"\n");
	    }
	    else {
	      finalscript.write(BJSscriptcommand+"\n");
	    }
	  }
	}
	if (BJSwritescript) {
	  finalscript.close();
	  if ((System.getProperty("file.separator")).equals("/")) {
	    (Runtime.getRuntime()).exec("chmod 700 "+finalscriptname);
	  }
	}
      }
      catch (Exception ee) { System.out.println(ee);};
    }

  /** Get the default settings in the BatchJobScheduler format. This 
      method can be used to reconstruct an input file for the scheduler. 
  */
  public static String getDefaultSettings()
    {
      StringBuffer res = new StringBuffer();
      BatchJobScheduler BJS = new BatchJobScheduler(null);

      res.append("#################################################################\n");
      res.append("# ------------- Settings for the BatchJobScheduler --------------\n");
      res.append("# Number of runs for each set of parameters\n");
      res.append("BJSruns = "+BJS.BJSruns+"\n");
      res.append("\n");
      res.append("# How to arrange the data when stored\n");
      res.append("#  0 = Do not store any data for this run.\n");
      res.append("#  1 = Save all datafiles in the same directory specified by BJSdatadir.\n");
      res.append("#  2 = Create a subdir for each runseries (a runseries consist of BJSruns runs)\n");
      res.append("#      and save the files in that directory.\n");
      res.append("BJSsavemethod = "+BJS.BJSsavemethod+"\n");
      res.append("\n");
      res.append("# Save each single run in its own file (BJSautonaming will be used to generate\n");
      res.append("# filenames.) \n");
      res.append("BJSsavesingle = "+BJS.BJSsavesingle+"\n");
      res.append("\n");
      res.append("# Save each runseries in its own file. (Filename will be generated from the\n");
      res.append("# overall runseries-number like <basefilename>_<runseries-number>.dat)\n");
      res.append("BJSsaverunseries = "+BJS.BJSsaverunseries+"\n");
      res.append("\n");
      res.append("# Save all data in a single file called <basefilename>_all.dat\n");
      res.append("BJSsaveallinone = "+BJS.BJSsaveallinone+"\n");
      res.append("\n");
      res.append("# Autonaming of generated files\n");
      res.append("#  0 = Append run number to filename. The necessary number of leading zero's\n");
      res.append("#      will be prepended in the number.\n");
      res.append("#      FORMAT:  <basefilename>_<runnumber>.dat\n");
      res.append("#      EXAMPLE: multipopstat_0037.dat\n");
      res.append("#  1 = Append grouped run number to filename. This mode will group the\n");
      res.append("#      files according to the value given in BJSruns.\n");
      res.append("#      FORMAT:  <basefilename>_<runseries-number>_<current BJSrun>.dat\n");
      res.append("#      EXAMPLE: multipopstat_03_09.dat\n");
      res.append("#  2 = Append run completion datetime to filename.\n");
      res.append("#      FORMAT:  <basefilename>_<completion datetime>.dat\n");
      res.append("#      EXAMPLE: multipopstat_11-08-1999_123452.dat\n");
      res.append("#  3 = Append run completion datetime (sorted and shortened) to filename.\n");
      res.append("#      By using this format the files will be ordered in increasing order\n");
      res.append("#      when the directory is listed (ls).\n");
      res.append("#      FORMAT:  <basefilename>_<completion datetime>.dat\n");
      res.append("#      EXAMPLE: multipopstat_19990811123452.dat\n");
      res.append("BJSautonaming = "+BJS.BJSautonaming+"\n");
      res.append("\n");
      res.append("# Where to store the data. \n");
      res.append("BJSdatadir = \""+BJS.BJSdatadir+"\"\n");
      res.append("\n");
      res.append("# Create a directory for each time you start the algorithm. \n");
      res.append("# 0 = Do not create a directory.\n");
      res.append("# 1 = Append date to BJSdatadir. (See below - name like BJSautonaming = 1)\n");
      res.append("# 2 = Append sorted and shorteded date to BJSdatadir.\n");
      res.append("#\n");
      res.append("# This works the following way: \n");
      res.append("# If BJSdatadir = \"results/\", BJScreatedir = \"multi\" and BJScreatedirmethod = 2\n");
      res.append("# then the data will be stored in e.g. \"results/multi_19990811123452/\"\n");
      res.append("BJScreatedirmethod = "+BJS.BJScreatedirmethod+"\n");
      res.append("\n");
      res.append("# The basename used when creating the directory.\n");
      res.append("BJScreatedir = \""+BJS.BJScreatedir+"\"\n");
      res.append("\n");
      res.append("# Delimiter between items/columns \n");
      res.append("BJScolumndelim = \"\\t\"\n");
      res.append("\n");
      res.append("# Delimiter between rows \n");
      res.append("BJSrowdelim = \"\\n\"\n");
      res.append("\n");
      res.append("# Build a script for processing the data after all jobs are done.\n");
      res.append("Can be used to format and plot data. The scheduler will generate \n");
      res.append("# two cd-commands to get into each data directory. \n");
      res.append("# Write the commands so they can be executed from the data directory.\n");
      res.append("BJSscriptcommand = \"\"\n");
      res.append("\n");      
      return res.toString();
    }
 
  /** Perform the current job. */
  private void performJob(int curindex)
    {
      int i;
      FileWriter fw;
      String progressfilename;
      
      if (curindex<currentsteppers.length) {
	      
	((BJSStepper)currentsteppers[curindex]).reset();
	while(!((BJSStepper)currentsteppers[curindex]).done()) {
	  ((BJSStepper)currentsteppers[curindex]).getNext();
	  performJob(curindex+1);
	}
      }
      else {
	for (i=0;i<currentsteplines.length;i++) {
	  currentsteplines[i].updateHashtable(batchjobparams);
	}

	((BatchJobEA)theEA).setSettings(batchjobparams);
	((BatchJobEA)theEA).initializeRunSeries();

	for (i=0;i<BJSruns;i++) {
	  currentrun++;
	     try {
	      // added by TK 19/01/2000
	      progressfilename = "runprogress";
	      fw = new FileWriter(BJSdatadir+progressfilename,currentrun>1);
		  fw.write("Run "+RKUStringUtils.prependZeros(currentrun, numberofruns)+" of "+numberofruns+"  "+calcEstimatedTime()+"\n");
	      fw.close();
       } catch (Exception ee) { System.out.println(ee);};
	  
	  if (verbosemode) {
	    System.out.println("Run "+RKUStringUtils.prependZeros(currentrun, numberofruns)+" of "+numberofruns+"  "+calcEstimatedTime());
	  }
	  ((BatchJobEA)theEA).initializeAndStartRun();
	  ((BatchJobEA)theEA).finalizeRun();

	  System.gc();

	  // Store the data.
	  if (BJSsavemethod!=0) {
	    saveData(((BatchJobEA)theEA).getDataAsText());
	  }

	}
	((BatchJobEA)theEA).finalizeRunSeries();
      }
    }

  /** Prepare to save the data to disk. */
  private void prepareSave()
    {
      File adir;
      Calendar now = GregorianCalendar.getInstance();
      FileWriter fw;
      String batchfilename;

      try {
	if (BJSdatadir.charAt(BJSdatadir.length()-1) != '/')
	  BJSdatadir = BJSdatadir+"/";

	if (BJSsavemethod!=0) {
	  switch(BJScreatedirmethod) {
	  case 0:
	    adir = new File(BJSdatadir);
	    if (!adir.isDirectory() || !adir.canWrite()) {
	      System.out.println("Datadirectory \""+BJSdatadir+"\" not found. Please create the directory or make sure the permissions are set.");
	      System.exit(0);
	    }
	    break;
	  case 1:
	    BJSdatadir = BJSdatadir+BJScreatedir+"_"+RKUStringUtils.prependZeros(now.get(Calendar.DAY_OF_MONTH),31)+"-"+RKUStringUtils.prependZeros(now.get(Calendar.MONTH)+1,12)+"-"+RKUStringUtils.prependZeros(now.get(Calendar.YEAR),4000)+"_"+RKUStringUtils.prependZeros(now.get(Calendar.HOUR_OF_DAY),24)+RKUStringUtils.prependZeros(now.get(Calendar.MINUTE),60)+RKUStringUtils.prependZeros(now.get(Calendar.SECOND),60)+"/";
	    adir = new File(BJSdatadir);
	    adir.mkdirs();
	    break;
	  case 2:
	    
	    BJSdatadir = BJSdatadir+BJScreatedir+"_"+getNowCompressed()+"/";
	    adir = new File(BJSdatadir);
	    adir.mkdirs();
	    break;
	  }
		
	  // Write the batchjobfile to the destination
	  batchfilename = (new File(batchjobfilename)).getName();
	  fw = new FileWriter(BJSdatadir+batchfilename);
	  for (Enumeration ennum=rawbatchjobfile.elements();ennum.hasMoreElements();) {
	    fw.write((ennum.nextElement()).toString());
	  }
	  fw.close();
	    
	}
      }
      catch (Exception ee) { System.out.println(ee);};
    }

  /** Get the current date time as a compressed string. */
  public String getNowCompressed()
    {
      Calendar now = GregorianCalendar.getInstance();

      return ""+now.get(Calendar.YEAR)+RKUStringUtils.prependZeros(now.get(Calendar.MONTH)+1,12)+RKUStringUtils.prependZeros(now.get(Calendar.DAY_OF_MONTH),31)+"_"+RKUStringUtils.prependZeros(now.get(Calendar.HOUR_OF_DAY),24)+RKUStringUtils.prependZeros(now.get(Calendar.MINUTE),60)+RKUStringUtils.prependZeros(now.get(Calendar.SECOND),60);
    }


  boolean firstsave = true;

  /** Save the recorded data to disk. */
  private void saveData(Object[] data)
    {
      int i,j;
      ConvertToText curdata;
      File newdir;
      String dataasstring, headingsasstring;
      String singlefilename = "A", runseriesfilename = "AA", dirname = "B";
      String allinonefilename = "AAA";
      Calendar now = Calendar.getInstance();
      FileWriter fw;
      String prependheader, prependdata;
      File afile;
      int runseriesnumber;
      String progressfilename;

      prependheader = getPrependHeading();
      prependdata = getPrependData();
	
      try {
	switch (BJSsavemethod) {
	case 1: // Save all in same directory

	  // If Run series are saved then save info on varied vars
	  if (BJSsaverunseries) {
	    runseriesnumber = ((currentrun-1)/BJSruns)+1;
	      
	    if ((currentrun-1)%BJSruns==0) {
	      runseriesfilename = "runseriesinfo_"+RKUStringUtils.prependZeros(runseriesnumber,(numberofruns/BJSruns)+1)+".dat";
	      fw = new FileWriter(BJSdatadir+runseriesfilename);
	      for (i=0;i<currentsteplines.length;i++) {
		fw.write(currentsteplines[i].varname+" ="+currentsteplines[i].toString()+"\n");
	      }
	      fw.close();
	      
	    }
	  }


	  for (i=0; i<data.length;i++) {
	    curdata = (ConvertToText)data[i];
	    dataasstring = curdata.convertDataToDelimited(BJScolumndelim, BJSrowdelim,prependdata);
	    headingsasstring = curdata.convertHeadingsToDelimited(BJScolumndelim, BJSrowdelim,prependheader);

	    // All in one file
	    if (BJSsaveallinone) {
	      allinonefilename = curdata.getBasefilename()+"_all.dat";

	      fw = new FileWriter(BJSdatadir+allinonefilename, !firstsave);
	      if (firstsave) {
		fw.write(headingsasstring);
	      }
	      fw.write(dataasstring);
	      fw.close();
	    };

	    // Run series
	    if (BJSsaverunseries) {
	      runseriesnumber = ((currentrun-1)/BJSruns)+1;
	      
	      // Remove old datafiles
	      if (currentrun==1) {
		for (j=1;j<(numberofruns/BJSruns)+1;j++) {
		  runseriesfilename = curdata.getBasefilename()+"_"+RKUStringUtils.prependZeros(j,(numberofruns/BJSruns)+1)+".dat";
		  afile = new File(BJSdatadir+runseriesfilename);
		  afile.delete();
		}
	      }

	      runseriesfilename = curdata.getBasefilename()+"_"+RKUStringUtils.prependZeros(runseriesnumber,(numberofruns/BJSruns)+1)+".dat";
			
	      fw = new FileWriter(BJSdatadir+runseriesfilename, !((currentrun-1)%BJSruns==0));
	      if ((currentrun-1)%BJSruns==0) {
		fw.write(headingsasstring);
	      }
	      fw.write(dataasstring);
	      fw.close();
	    }

	    // Single saving
	    if (BJSsavesingle) {
	      switch (BJSautonaming) {
	      case 0:
		singlefilename = curdata.getBasefilename()+"_"+RKUStringUtils.prependZeros(currentrun,numberofruns)+".dat";
		break;
	      case 1:
		singlefilename = curdata.getBasefilename()+"_"+RKUStringUtils.prependZeros(((currentrun-1)/BJSruns)+1,(numberofruns/BJSruns)+1)+"_"+RKUStringUtils.prependZeros(((currentrun-1)%BJSruns)+1,BJSruns)+".dat";
		break;
	      case 2:
		singlefilename = curdata.getBasefilename()+"_"+RKUStringUtils.prependZeros(now.get(Calendar.DAY_OF_MONTH),31)+"-"+RKUStringUtils.prependZeros(now.get(Calendar.MONTH)+1,12)+"-"+RKUStringUtils.prependZeros(now.get(Calendar.YEAR),4000)+"_"+RKUStringUtils.prependZeros(now.get(Calendar.HOUR_OF_DAY),24)+RKUStringUtils.prependZeros(now.get(Calendar.MINUTE),60)+RKUStringUtils.prependZeros(now.get(Calendar.SECOND),60)+"/";
		break;
	      case 3:
		BJSdatadir = BJSdatadir+"_"+now.get(Calendar.YEAR)+RKUStringUtils.prependZeros(now.get(Calendar.MONTH)+1,12)+RKUStringUtils.prependZeros(now.get(Calendar.DAY_OF_MONTH),31)+RKUStringUtils.prependZeros(now.get(Calendar.HOUR_OF_DAY),24)+RKUStringUtils.prependZeros(now.get(Calendar.MINUTE),60)+RKUStringUtils.prependZeros(now.get(Calendar.SECOND),60)+"/";
		break;
	      }

	      fw = new FileWriter(BJSdatadir+singlefilename);
	      fw.write(headingsasstring);
	      fw.write(dataasstring);
	      fw.close();
	    }
	  }
	  firstsave = false;
	  break;
	  //	    System.out.println(curdata.convertToDelimited(BJScolumndelim, BJSrowdelim));
	}
      }
      catch (Exception ee) { System.out.println(ee); };
    }

  private String getPrependHeading()
    {
      StringBuffer res = new StringBuffer();

      res.append("Run");
      res.append(BJScolumndelim);
      res.append("Series");
      res.append(BJScolumndelim);
      /*
      for (int i=0;i<currentsteppers.length;i++) {
	res.append(((BJSStepper)currentsteppers[i]).getName());
	res.append(BJScolumndelim);
      }
      */
      return res.toString();
    }

  private String getPrependData()
    {
      StringBuffer res = new StringBuffer();

      res.append(""+currentrun);
      res.append(BJScolumndelim);
      res.append(""+((currentrun-1)/BJSruns+1));
      res.append(BJScolumndelim);

      /*
      for (int i=0;i<currentsteppers.length;i++) {
	res.append(((BJSStepper)currentsteppers[i]).toShortString());
	res.append(BJScolumndelim);
      }
      */
      return res.toString();
    }

  /** Parse the args on the command line. */
  private void parseArgs(String[] arguments)
    {
      int i;
      RKUVector filenames = new RKUVector();

      i=0;
      while (i<arguments.length) {
	if (arguments[i].equals("-f")) {
	  if (i+1<arguments.length) {
	    i++;
	    while (i<arguments.length && !arguments[i].startsWith("-")) {
	      filenames.addElement(arguments[i]);
	      i++;
	    };
	    i--;
	    allbatchjobfiles = new String[filenames.size()];
	    filenames.copyInto(allbatchjobfiles);

	    if (allbatchjobfiles.length>1) {
	      System.out.println("Executing the following batchfiles:");
	      for (i=0;i<allbatchjobfiles.length;i++) {
		System.out.println("  "+allbatchjobfiles[i]);
	      }
	      System.out.println("\n----- Starting batchruns -----");
	    }
	  }
	  else {
	    System.out.println("No batch files given after option -f");
	    System.exit(0);
	  }
	}
	else if (arguments[i].equals("-q")) {
	  verbosemode = false;
	}
	else if (arguments[i].equals("-s")) {
	  BJSwritescript = true;
	}
	else if (arguments[i].equals("-h")) {
	  System.out.println("The batchjob scheduler v1.01 Copyright 1999 The EvaLife team (www.evalife.dk).\nOptions:\n  -f <filenames> Run the scheduler with the batchfiles <filenames>\n  -s Produce a Datascript file for handling data.\n  -q Run quiet mode.\n  -h This message.");
	  System.exit(0);
	}

	i++;
      }
    }

  /** Load the batchjob input file and put the lines in the vector. */
  private void loadBatchFile(String filename)
    {
      FileReader fread;
      BufferedReader bread;
      String curline;
      StringTokenizer stoken;
      String varname, value;
      RKUVector cursteppers = new RKUVector();
      RKUVector cursteplines = new RKUVector();
      int linenumber = 1;

      try {
	if (filename!=null) {
	  fread = new FileReader(filename);
	  bread = new BufferedReader(fread);
	  
	  batchjoborder = new Vector();
	  
	  while((curline = bread.readLine())!=null) {
	    rawbatchjobfile.addElement(new String(curline+"\n"));
	    if (curline.length()>2) {
	      if (curline.charAt(0)!='#') {
		      
		stoken = new StringTokenizer(curline,"=");
		if (stoken.hasMoreTokens()) {
		  varname = stoken.nextToken();
		  varname = varname.trim();
		  if (stoken.hasMoreTokens()) {
		    value = stoken.nextToken();
		    value = value.trim();

		    /* If the line contains a special BJS command
		       deal with it. */
		    if (varname.equals("BJSruns")) {
		      BJSruns = (new Integer(value)).intValue();
		    }
		    else if (varname.equals("BJSsavemethod")) {
		      BJSsavemethod = (new Integer(value)).intValue();
		    }
		    else if (varname.equals("BJSsavesingle")) {
		      BJSsavesingle = (new Boolean(value)).booleanValue();
		    }
		    else if (varname.equals("BJSsaverunseries")) {
		      BJSsaverunseries = (new Boolean(value)).booleanValue();
		    }
		    else if (varname.equals("BJSsaveallinone")) {
		      BJSsaveallinone = (new Boolean(value)).booleanValue();
		    }
		    else if (varname.equals("BJSautonaming")) {
		      BJSautonaming = (new Integer(value)).intValue();
		    }
		    else if (varname.equals("BJScreatedirmethod")) {
		      BJScreatedirmethod = (new Integer(value)).intValue();
		    }
		    else if (varname.equals("BJSdatadir")) {
		      BJSdatadir = RKUStringUtils.stripQuotes(value);
		    }
		    else if (varname.equals("BJScreatedir")) {
		      BJScreatedir = RKUStringUtils.stripQuotes(value);
		    }
		    else if (varname.equals("BJSscriptcommand")) {
		      BJSscriptcommand = RKUStringUtils.stripQuotes(value);
		    }
		    else if (varname.equals("BJScolumndelim")) {
		      BJScolumndelim = RKUStringUtils.stripQuotes(value);
		      if (BJScolumndelim.equals("\\t"))
			BJScolumndelim = "\t";
		      else if(BJScolumndelim.equals("\\n"))
			BJScolumndelim = "\n";
		      else if(BJScolumndelim.equals("\\n\\r"))
			BJScolumndelim = "\n\r";
		      else if(BJScolumndelim.equals("\\r\\n"))
			BJScolumndelim = "\r\n";
		    }
		    else if (varname.equals("BJSrowdelim")) {
		      BJSrowdelim = RKUStringUtils.stripQuotes(value);
		      if (BJSrowdelim.equals("\\t"))
			BJSrowdelim = "\t";
		      else if(BJSrowdelim.equals("\\n"))
			BJSrowdelim = "\n";
		      else if(BJSrowdelim.equals("\\n\\r"))
			BJSrowdelim = "\n\r";
		      else if(BJSrowdelim.equals("\\r\\n"))
			BJSrowdelim = "\r\n";
		    }

		    else {
		      batchjobparams.put(varname,value);

		      /* If the variable contains some stepvalue
			 insert it in the cursteppers vector. */
		      if (curline.indexOf('!')>=0) {

			// Currently we only handle varsteppers
			cursteplines.addElement(new BJSStepline(curline, linenumber, cursteppers));
		      }
		    }
		  }
		  batchjoborder.addElement(curline);
		}
	      }
	    }
	    linenumber++;
	  }
	  currentsteppers = cursteppers.getElementsAsArray();
	  currentsteplines = new BJSStepline[cursteplines.size()];

	  cursteplines.copyInto(currentsteplines);
	}
      }
      catch(Exception e) { System.out.println(e);};
    }

  private long firstmill, lastmill, currentmill, sincelast;
  private int currentrun, numberofruns;

  /** Calculate the estimated time to completion. */ 
  public String calcEstimatedTime()
    {
      String dateString = "";

      if (firstmill==0)
	firstmill = System.currentTimeMillis();
      else {
	if(currentrun<=numberofruns) {
	  //	  lastmill = currentmill;
		
	  currentmill = System.currentTimeMillis();
		
	  sincelast = ((currentmill-firstmill)/currentrun)*(numberofruns-(currentrun-1));
	  dateString ="Remaining time: "+RKUStringUtils.timeDifference(currentmill, currentmill+sincelast)+"  Completion time: "+RKUStringUtils.timeFormatter(new Date(currentmill + sincelast));
	}
      }
      return dateString;
    }

  /** Calculate the total number of runs. This is for the estimation of 
      completion time. */
  public void calcTotalRuns()
    {
      numberofruns = BJSruns;

      for (int i=0;i<currentsteppers.length;i++) {
	numberofruns *= ((BJSStepper)currentsteppers[i]).getNumberofSteps();
      }
    }
}


class BJSStepline
{
  String[] textparts;
  BJSStepper[] varparts;
  int numberoftokens;
  String varname;

  public BJSStepline(String curline, int linenumber, RKUVector steppers)
    {
      super();
      String[] stok;
      String[] stok2;
      BJSStepper curstepper;
      String curtok;
      int i;

      stok2 = RKUStringUtils.split(curline, "=");
      if (stok2.length!=2) {
	System.out.println("Error in batchfile at line "+linenumber+". \nThe format of a line is <variablename> = <value>");
	System.exit(0);
      }
      else {
	varname = (stok2[0]).trim();
	curline = stok2[1].trim();
	stok = RKUStringUtils.split(curline, "!");
	if ((stok.length%2)!=1) {
	  System.out.println("Error in batchfile at line "+linenumber+". \nThe format of a varying number is !<lower>,<upper>,<stepsize>!\nRemember opening/closing exclamation mark (!)"+curline);
	  System.exit(0);
	}
	else {
	  textparts = new String[(stok.length-1)/2+1];
	  varparts = new BJSStepper[(stok.length-1)/2];
	  
	  i = 0;
	  numberoftokens = stok.length;
	  while (i<numberoftokens) {
	    curtok = stok[i];
	    
	    if (i%2 == 0) 
	      textparts[i/2] = curtok;
	    else {
	      if (curtok.startsWith("TEXT")) {
		// We have a textstepper
		curstepper = new BJSTextStepper(curtok,linenumber);
	      }
	      else {
		// Assume it to be a variable stepper
		curstepper = new BJSVariableStepper(curtok,linenumber);
	      }
	      steppers.addElement(curstepper);
	      varparts[(i-1)/2] = curstepper;
	    };
	    i++;
	  };
	};
      };
    };

  public void updateHashtable(Hashtable htable)
    {
      htable.put(varname, this.toString());
    };

  public String toString()
    {
      StringBuffer sbuf = new StringBuffer();
      int i;

      for(i=0;i<numberoftokens;i++) {
	if (i%2==0)
	  sbuf.append(textparts[i/2]);
	else
	  sbuf.append(((varparts[(i-1)/2].getValue()).toString()).trim());
      }
      return sbuf.toString();
    };
}

interface BJSStepper
{
  public void reset();
  public Object getNext();
  public Object getValue();
  public String getName();
  public boolean done();
  public int getNumberofSteps();
  public String toShortString();

}

/** This class is used to iterate a variable though an interval. */
class BJSVariableStepper implements BJSStepper
{
  double lower, upper, stepsize, curvalue;
  int numberofsteps;
  boolean increasestep;
  String variablename;
  boolean isdouble;
  boolean firstread = false;
  int curstep;

  /** str must be on the form: <varname> = <lower>!<upper>!<stepsize> */
  public BJSVariableStepper(String str, int linenumber)
    {
      super();
      StringTokenizer stoken;
      boolean succes = false;

      // Check whether or not this is a double or an int stepper
      if (str.indexOf('.')>0)
	isdouble = true;
      else
	isdouble = false;

      stoken = new StringTokenizer(str,",");
      if (stoken.countTokens()==3) {
	lower = (new Double((stoken.nextToken()).trim())).doubleValue();
	upper = (new Double((stoken.nextToken()).trim())).doubleValue();
	stepsize = (new Double((stoken.nextToken()).trim())).doubleValue();
	succes = true;
      }

      if (!succes) {
	System.out.println("Error in batchfile at line "+linenumber+". \nThe format of a varying value is !<lower>,<upper>,<stepsize>!"+str);
	System.exit(0);
      }

      if (lower<upper) {
	increasestep = true;
	curvalue = lower;
	stepsize = Math.abs(stepsize);
      }
      else {
	increasestep = false;
	curvalue = lower;
	stepsize = -Math.abs(stepsize);
      }

      numberofsteps = ((int)(Math.rint((upper-lower)/stepsize)))+1;
      curstep = 0;
    }

  public void reset()
    {
      curvalue = lower;
      curstep=0;
      firstread = false;
    }

  public Object getNext()
    {
      if (firstread)
	curvalue += stepsize;

      firstread = true;
      curstep++;
      return new Double(curvalue);
    }

  public Object getValue()
    {
      return new Double(curvalue);
    }

  public String getName()
    {
      return variablename;
    }
    
  public boolean done()
    {
      return curstep>=numberofsteps;
    }

  public int getNumberofSteps()
    {
      return numberofsteps;
    }

  public String toString()
    {
      return ""+curvalue;
    }

  public String toShortString()
    {
      if (isdouble)
	return ""+curvalue;
      else
	return ""+(int)(Math.rint(curvalue));
    }

}

/** This class is used to iterate a through list of textelements. */
class BJSTextStepper implements BJSStepper
{
  int numberofsteps;
  boolean firstread = false;
  int curstep;
  String[] allvalues;

  public BJSTextStepper(String str, int linenumber)
    {
      super();
      StringTokenizer stoken;
      boolean succes = false;
      String curtok;
      int i;

      if (str.indexOf("#TEXT#")>0) {
	allvalues = RKUStringUtils.split((str.trim()).substring(5,str.length()),"#TEXT#");
      }
      else {
	allvalues = RKUStringUtils.split((str.trim()).substring(5,str.length()),",");
      }

      succes = true;

      for (i=0;i<allvalues.length;i++) {
	allvalues[i] = allvalues[i].trim();
      }

      numberofsteps = allvalues.length;

      /*
      if (str.indexOf("#TEXT#")>0) {
	stoken = new StringTokenizer(str.substring(5,str.length()),"#TEXT#");
	
      }
      else {
	stoken = new StringTokenizer(str.substring(5,str.length()),",");
      }
      if (stoken.countTokens()>0) {

	allvalues = new String[stoken.countTokens()];
	for (i=0;i<allvalues.length;i++) {
	  allvalues[i] = (stoken.nextToken()).trim();
	}

	numberofsteps = allvalues.length;
      }
      */
      if (!succes) {
	System.out.println("Error in batchfile at line "+linenumber+". \nThe format of a list of texts is:\n  !TEXT,<text1>,<text2>,...,<textn>! or \n  !TEXT,<text1>#TEXT#<text2>#TEXT#...#TEXT#<textn>!"+str);
	System.exit(0);
      }

      curstep = 0;
    }

  public void reset()
    {
      curstep=0;
      firstread = false;
    }

  public Object getNext()
    {
      if (firstread)
	curstep++;

      firstread = true;
      return allvalues[curstep];
    }

  public Object getValue()
    {
      return allvalues[curstep];
    }

  public String getName()
    {
      return "";
    }
    
  public boolean done()
    {
      return curstep+1>=numberofsteps;
    }

  public int getNumberofSteps()
    {
      return numberofsteps;
    }

  public String toString()
    {
      return allvalues[curstep];
    }

  public String toShortString()
    {
      return toString();
    }

}



